//
//  vdTMXNode.m
//  Void Dead
//
//  Created by Sidney Just on 31.01.10.
//
//  Copyright © 2009 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import "vdTMXNode.h"

#define UNSUPPORTED 1
#define READDATA 2

#define ORTHOGONAL 1

#define BASE64 1
#define GZIP 2

@implementation vdTMXNode
@synthesize mapWidth, mapHeight, tileHeight, tileWidth;

- (void)setNodeShader:(vdShader *)_shd {
	for(int i=0; i<contLayer; i++)
	{
		container[i].nodeShader = _shd;
	}
	
	nodeShader = _shd;
}

- (vdTMXLayer *)nextLayer:(vdTMXLayer *)_layer {
	if(contLayer > 0)
	{
		if(!_layer)
			return container[0];
		
		for(int i=0; i<contLayer; i++)
		{
			if(container[i] == _layer)
			{
				if(i < contLayer-1)
				{
					return container[i+1];
				} else {
					return NULL;
				}
			}
		}
	}
	return NULL;
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
	if(mapLoadState == UNSUPPORTED)
		return;
	
	mapLoadState = 0;
	
	if([elementName isEqualToString:@"map"])
	{
		if(![[attributeDict valueForKey:@"version"] isEqualToString:@"1.0"])
		{
			vdLog(@"Unsupported TMX Version: %@", [attributeDict valueForKey:@"version"]);
			mapLoadState = UNSUPPORTED;
		}
			
		NSString *_orientation = [attributeDict valueForKey:@"orientation"];
		
		if([_orientation isEqualToString:@"orthogonal"])
		{
			mapType = ORTHOGONAL;
		} else {
			vdLog(@"Unsupported orientation: %@", [attributeDict valueForKey:@"orientation"]);
			mapLoadState = UNSUPPORTED;
		}
		
		if([attributeDict valueForKey:@"width"])
		{
			mapWidth = [[attributeDict valueForKey:@"width"] intValue];
		}
		
		if([attributeDict valueForKey:@"height"])
		{
			mapHeight = [[attributeDict valueForKey:@"height"] intValue];
		}
		
		if([attributeDict valueForKey:@"tilewidth"])
		{
			tileWidth = [[attributeDict valueForKey:@"tilewidth"] intValue];
		}
		
		if([attributeDict valueForKey:@"tileheight"])
		{
			tileHeight = [[attributeDict valueForKey:@"tileheight"] intValue];
		}
	}
	else if([elementName isEqualToString:@"tileset"])
	{
		if([attributeDict valueForKey:@"firstgid"])
		{
			firstGid = [[attributeDict valueForKey:@"firstgid"] intValue];
		}
		
		if([attributeDict valueForKey:@"tilewidth"])
		{
			tileWidth = [[attributeDict valueForKey:@"tilewidth"] intValue];
		}
		
		if([attributeDict valueForKey:@"tileheight"])
		{
			tileHeight = [[attributeDict valueForKey:@"tileheight"] intValue];
		}
	}
	else if([elementName isEqualToString:@"image"])
	{
		[textures addTexture:[attributeDict valueForKey:@"source"]];
		texture = [[vdTexManager sharedManager] loadTexture:[attributeDict valueForKey:@"source"]];
	}
	else if([elementName isEqualToString:@"layer"]) 
	{
		contLayer += 1;
		container = (vdTMXLayer **)realloc(container, contLayer*sizeof(vdTMXLayer *));
		
		container[contLayer-1] = [[vdTMXLayer alloc] initWithName:[attributeDict valueForKey:@"name"]];
		
		activeLayer = container[contLayer-1];
		activeLayer.texture = texture;
		activeLayer.tileWidth = tileWidth;
		activeLayer.tileHeight = tileHeight;
		activeLayer.firstGid = firstGid;
		
		if([attributeDict valueForKey:@"width"])
		{
			activeLayer.layerWidth = [[attributeDict valueForKey:@"width"] intValue];
		}
		if([attributeDict valueForKey:@"height"])
		{
			activeLayer.layerHeight = [[attributeDict valueForKey:@"height"] intValue];
		}
		
		if([attributeDict valueForKey:@"opacity"])
		{
			activeLayer.color->alpha = [[attributeDict valueForKey:@"opacity"] floatValue];
		}
		
		[self addChild:activeLayer];
		
	}
	else if([elementName isEqualToString:@"data"]) 
	{
		mapLoadState = READDATA;
		
		if([[attributeDict valueForKey:@"encoding"] isEqualToString:@"base64"])
		{
			compression |= BASE64;
		} else {
			vdLog(@"Unsupported Map encoding: %@", [attributeDict valueForKey:@"encoding"]);
			mapLoadState = UNSUPPORTED;
		}

		if([[attributeDict valueForKey:@"compression"] isEqualToString:@"gzip"])
		{
			compression |= GZIP;
		} else {
			vdLog(@"Unsupported Map compression: %@", [attributeDict valueForKey:@"compression"]);
			mapLoadState = UNSUPPORTED;
		}
		
		inputStr = [[NSMutableString alloc] init];
	}
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
   if(mapLoadState == READDATA && inputStr)
   {
		[inputStr appendString:string];
   }
}

- (NSString *)trimParsedString {
	if(inputStr)
	{
		NSString *temp;
		
		NSMutableString *newString = [[NSMutableString alloc] init];
		NSRange tempRange;
		
		for(int i=0; i<[inputStr length]; i++)
		{
			tempRange.location = i;
			tempRange.length = 1;
			
			temp = [inputStr substringWithRange:tempRange];
			
			BOOL found = NO;
			
			for(int i=0; i<[characterArray count]; i++)
			{
				if([temp isEqualToString:[characterArray objectAtIndex:i]])
					found = YES;
			}
			
			if(!found)
				[newString appendString:temp];
		}
		
		return [newString autorelease];
	}
	
	return @"NULL";
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
	if([elementName isEqualToString:@"data"]) 
	{
		NSString *decodedStr = [[NSString alloc] initWithString:[self trimParsedString]];
		[inputStr release];
		inputStr = NULL;
		
		if(compression & BASE64)
		{
			unsigned char *buffer;
			
			int len = base64Decode((unsigned char *)[decodedStr UTF8String], [decodedStr length], &buffer);
			
			activeLayer.tiles = (unsigned int*)buffer;
			
			if(compression & GZIP)
			{
				unsigned char *deflated;
				inflateMemory(buffer, len, &deflated);
				
				activeLayer.tiles = (unsigned int*)deflated;
				free(buffer);
			}
			
			[activeLayer createMap];
		}
		
		[decodedStr release];
	}
}

- (void)loadMap:(NSString *)file {
	compression = 0;
	inputStr = NULL;
	
	mapLoadState = 0;
	mapType = 0;
	mapWidth = 0;
	mapHeight = 0;
	tileWidth = 0;
	tileHeight = 0;
	firstGid = 0;
	
	activeLayer = NULL;
	
	characterArray = [[NSArray alloc] initWithObjects:@"\t", @"\n", @" ", nil];
	
	container = (vdTMXLayer **)malloc(sizeof(vdTMXLayer *));
	
	NSData *xmlData = [[NSData alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], file]];
	parser = [[NSXMLParser alloc] initWithData:xmlData];
	
	[parser setShouldProcessNamespaces:NO];
	[parser setShouldReportNamespacePrefixes:NO];
	[parser setShouldResolveExternalEntities:NO];
	[parser setDelegate:self];
	[parser parse];
	
	NSError *error;
	if((error = [parser parserError]))
	{
		vdLog(@"XML Error %@ in %@", error, self);
		
		free(container);
		container = NULL;
	}
	
	[parser release];
	[xmlData release];
	
	size_x = mapWidth * tileWidth;
	size_y = mapHeight * tileHeight;
	
	[characterArray release];
}

- (vdTMXLayer *)getLayerWithName:(NSString *)str {
	for(int i=0; i<contLayer; i++)
	{
		if([container[i].layerName isEqualToString:str])
			return container[i];
	}
	
	return NULL;
}

- (id)initWithMap:(NSString *)_map {
	if(self = [super init])
	{
		parser = NULL;
		container = NULL;
		contLayer = 0;
		
		texture = NULL;
		
		textures = [[vdTextureContainer alloc] init];
		
		[self loadMap:_map];
	}
	return self;
}

- (id)init {
	NSException *exception = [[NSException alloc] initWithName:@"Wrong init" reason:@"vdTMXNode doesn't work with init: use initWithMap: instead!" userInfo:nil];
	@throw exception;
	[exception release];

	return NULL;
}

- (void)dealloc {
	[self cleanChildren];
	
	if(container)
		free(container);
	
	[textures release];
	
	[super dealloc];
}

@end
